// helper
var dump = function(what) {
	if(typeof console != 'undefined')
		console.log(what);
};

// allow future times
jQuery.timeago.settings.allowFuture = true;

// start frontend
var Frontend = function() {
	var store = {
		url: false

	// transform links to support history api
	}, ajax = function(o) {
		var overwrite = {
			url: (o.fullurl ? o.fullurl : (store.url + o.url)),
			beforeSend: _loading(),

			// on success
			success: function(x) {
				if(x.success)
					return o.success ? o.success(x) : {};
				else
					return o.error ? o.error(x) : _boom(x);

			// on error
			}, error: function(a, b, c) {
				return o.error ? o.error(a, b, c) : _boom(a, b, c);
			}
		};

		// ajaxish
		$.ajax($.extend({}, o, overwrite)).always(_finished);

	// on error show a notification
	}, _boom = function(a) {
		// enable buttons on errors
		var b = $('button', 'div.page');
		b.removeAttr('disabled');

		// got errors key?
		if(a.errors) {
			// each error
			$.each(a.errors, function(k, v) {
				Frontend.noty({type: 'error', text: v});
			});

		// no errors key, just default error message then
		} else Frontend.noty({type: 'error', text: 'Internal server error'});

	// bind events
	}, _bind = function() {
		// on ajax click
		$('div.site').on('click', "a[data-to='ajax']", function(e) {
			e.preventDefault();
			Frontend.go($(this, 'div.site').attr('href'));

		// on mouse enter metro areas
		}).on('mouseenter', "div.metro > div", function() {
			$(this).find('div.over').stop(true, true).fadeOut('fast');

		// on mouse leave metro areas
		}).on('mouseleave', "div.metro > div", function() {
			$(this).find('div.over').stop(true, true).fadeIn();
		});

		// add waypoint
		$('div.noty', 'div.site').waypoint(function(direction) {
			if(direction === "down") $(this).parent().removeClass().addClass('sticky');
			else $(this).parent().removeClass().addClass('tycontainer');
		});

		// listen for the online event
		$('div.page', 'div.site').on('online', $.debounce(250, function() {
			if(Frontend.ctrl[Frontend.location.ctrl]['online'])
				Frontend.ctrl[Frontend.location.ctrl]['online']();
		}));

		// set focus flag
	    $(window).on('focus', function(event) {
			Frontend.focus = true;

		// unset focus flag
	    }).on('blur', function(event) {
			Frontend.focus = false;

		// prevent afk on key down
	    }).on('keydown', $.throttle(2500, true, Frontend.login))

	    // prevent afk on mouse move
	    .on('mousemove', $.throttle(2500, true, Frontend.login))

		// bind hotkeys
		.keySequenceDetector({
			// go afk or busy
			away: Frontend.socket.away,
			busy: Frontend.socket.busy,

			// login / logout
			login: function(){ Frontend.go('login'); },
			logout: Frontend.logout,

			// big controllers
			home: function(){ Frontend.go('index'); },
			apply: function(){ Frontend.go('apply'); },
			forum: function(){ Frontend.go('forum'); },
			search: function(){ Frontend.go('search'); },
			profile: function(){ Frontend.go('profile'); },
			status: function(){ Frontend.go('status'); },
			roster: function(){ Frontend.go('roster'); }

		// on resize adjust site height
		}).on('resize', $.throttle(250, _adjust));

		// on resize adjust site height
		$('div.page').on('resize', _adjust);

		// pulse every 5 seconds
		window.setInterval(_pulse, 5000);

		// refresh metro every 30 seconds
		window.setInterval(function() {
			Frontend.metro();
		}, 30000);

	// history api go function
	}, go = function(where) {
		var loc = /^http:\/\//.test(where) ? where : store.url + where;
		if(window.history && window.history.pushState) {
			Frontend.ajax({type: 'post', data: 'callback=build', fullurl: loc, success: function(data){
				_page(data);
				_simpleselect();
			}});

			if(loc != window.location.href)
				window.history.pushState({path: loc}, '', loc);
		} else window.location.href = loc;

	// history api change page function
	}, _page = function(data) {
		// try and update the page title
		document.title = data.location.title;
		try { document.getElementsByTagName('title')[0].innerHTML = data.location.title;
		} catch (Exception) {}

		// update page
		$('div.page', 'div.site').attr('id', data.location.ctrl);
		$('div.page', 'div.site').html($(data.html).html());

		// update menu
		$('ul.menu > li', 'div.site').removeClass();
		$("ul.menu > li#ctrl_"+ data.location.ctrl, 'div.site').addClass('active');

		// trigger event
		$('div.page', 'div.site').trigger('licked');

		// transform all no follow in extern links (not for pictures)
		$('a[rel="nofollow"]:not(.picture)').addClass('extern');

		// adjust the site height on page load
		_adjust();

		// update dates and times
		$("abbr").timeago();

		// if we have a temp noty close it
		if(window.TEMP_NOTY)
			window.TEMP_NOTY.close();

		// did we had to show a noty?
		delete window.LOGIN_LOCATION;
		if(!$.isEmptyObject(data.noty)) {
			window.TEMP_NOTY = Frontend.noty({type: data.noty.type, text: data.noty.text});
			window.LOGIN_LOCATION = window.location.href;
		}

		// spice location with a dash of javascript
		Frontend.location = $.extend({actual: window.location.href}, data.location);

		// tell socket io we are logging in
		Frontend.login();

		// init controller
		Frontend.ctrl[data.location.ctrl].init();

		// remove overlay
		$('div.overlay', 'body').fadeOut(150);

	// fix for simple select in label bug
	}, _simpleselect = function() {
		var stop = true;
		$('label.select', 'div.page form').on('mousedown', function(e) {
			stop = !$(this).children('div.simpleselect').hasClass('active');
		}).on('click', function(e) {
			if(stop) {
				e.stopPropagation();
				stop = false;
			} else stop = true;
		});

	// refresh metro
	}, metro = function(f) {
		Frontend.ajax({type: 'post', data: 'callback=build', url: 'metro', success: function(data){
			$('div.metro', 'div.site').html(data.html);

			// callback?
			if(f) f();
		}});

	// started loading
	}, _loading = function() {
		if($("#loadingbar").length)
			return;

		$("body").append("<div id='loadingbar'></div>");
		$("#loadingbar").addClass("waiting").append($("<dt/><dd/>"));
		$("#loadingbar").width((50 + Math.random() * 30) + "%");

	// finished loading
	}, _finished = function() {
		$("#loadingbar").width("101%").delay(200).fadeOut(400, function() {
			$(this).remove();
		});

	// socket.io login or de-afker
	}, login = function() {
		// window is not focused? abort
		if(!Frontend.focus)
			return;

		Frontend.active = new Date().getTime();
		Frontend.socket.login();

	// handle logout
	}, logout = function(redirect) {
		// do logout procedure
		Frontend.ajax({type: 'post', data: {}, url: 'metro/logout', success: function(data) {
			// send & broadcast message
			Frontend.noty({type: 'information', text: 'Successfully logged out!'});
			Frontend.socket.noty({type: 'information', text: data.user.name + ' logged out!'});

			// transform profile tile into auth tile
			Frontend.metro(function() {
				// tell socket we logged out and redirect
				Frontend.socket.logout();
				Frontend.go(redirect ? redirect : window.location.href);
			});
		}});

	// notifications, sexy style
	}, noty = function(data, sticky) {
		// call the notification
		var noty = $('div.noty', 'div.site').noty($.extend({
			maxVisible: 50,
			animation: {
				open: {opacity: 'toggle'},
				close: {opacity: 'toggle'},
				easing: 'swing',
				speed: 200
			},
			timeout: sticky ? false : 3000
		}, data));

		// return the notification object
		return noty;

	// noty area
	}, _adjust = function(bind) {
		var s = $('div.site', 'body');

		// change height
		if($(document).height() > $(window).height()) s.css('height', 'auto');
		else s.css('height', '100%');

	// a pulse function set at 5 seconds
	}, _pulse = function(delay) {
		// set away
		if(Frontend.active < new Date().getTime() - Frontend.away)
			Frontend.socket.away(true);

	// default announce function
	}, announce = function(result) {
		var found = false;

		// no result?
		if(!result)
			return;

		// no result locations?
		if(!(result.locations && result.locations.length > 0))
			return;

		// go through each location element
		$.each(result.locations, function(k, v) {
			var versus = v.split('@');

			// a little hack with page, if page = "_" that means any number can go there
			if(Frontend.location.ctrl == versus[0] && $.param(Frontend.location.path) == versus[1].replace('page=_', 'page=' + Frontend.location.path.page)) {
				found = true;
				return false;
			}
		});

		// return our findings
		return found;

	// useful functions used in other pages
	// ---------------------------------------------------

	// get document height
	}, height = function() {
		var D = document;
		return Math.max(
			Math.max(D.body.scrollHeight, D.documentElement.scrollHeight),
			Math.max(D.body.offsetHeight, D.documentElement.offsetHeight),
			Math.max(D.body.clientHeight, D.documentElement.clientHeight)
		);

	// see if element is scrolled into view
	}, pinpoint = function(elem) {
		var docViewTop = $(window).scrollTop();
		var docViewBottom = docViewTop + $(window).height();

		var elemTop = elem.offset().top;
		var elemBottom = elemTop + elem.height();

		return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));

	// ---------------------------------------------------
	// end useful functions

	// init
	}, __construct = function() {
		// keke
		dump("-------------------------------");
		dump("|  Sunny Web Platform  v0.3d  |");
		dump("-------------------------------");

		// set base url
		store.url = $('base').attr('href');

		// bind and load controller
		_bind();
		Frontend.go(window.location.href); // load default ctrl

		// autologin? broadcast it
		if($('div.site', 'body').data('autologin') == 'yes')
			Frontend.socket.noty({type: 'information', text: $('a#auth', 'div.metro').data('name') + ' logged in!'});

		// history api onpopstate
		if(window.history && window.history.pushState) {
			window.addEventListener('load', function() {
				setTimeout(function() {
					window.addEventListener('popstate', function() {
						Frontend.ajax({type: 'post', data: 'callback=build', fullurl: window.location.href, success: function(data){
							_page(data);
						}});
					});
				}, 0);
			});
		}
	};

	// public, yay
	return {
		init: __construct,
		ajax: ajax,
		go: go,
		metro: metro,
		loading: _loading,
		finished: _finished,
		login: login,
		logout: logout,
		noty: noty,
		announce: announce,
		pinpoint: pinpoint,
		height: height
	};
}();

// socket io
Frontend.socket = function() {
	var socket,
		store = {
			busy: false,
			away: false,
			sound: {
				noty: 'snd/noty.mp3'
			}

	// get user token
	}, _token = function() {
		return $('a#auth', 'div.metro').data('token');

	// preload sounds
	}, _sounds = function() {
		// the preloader function
		var preload = function(href, vol) {
			var sound = new Audio();

			sound.src = href;
			sound.preload = "auto";
			sound.volume = vol;

			return sound;
		};

		// preload noty sound
		store.sound.noty = preload(store.sound.noty, 0.3);

	// set user as away
	}, _away = function(id) {
		_update(id, 'away');

	// set user as busy
	}, _busy = function(id) {
		_update(id, 'busy');

	// set users as online
	}, _online = function(list) {
		// go though each user in the list
		$.each(list, function(k, v) {
			// already added on top?
			if(!$("div.avatar[data-id='" + v.id + "']", 'div.inside > div.top').length > 0) {
				// add user on top
				$('div.top', 'div.inside').append(
					'<div class="avatar" data-id="'+ v.id +'">'+
						'<a data-to="ajax" href="profile/view/'+ v.link +'" title="'+ v.name +' - Offline">'+
							'<div class="status offline"></div>'+
							'<img onerror="$(this).hide(); $(this).prev().css({background: \'#ddd\'});"'+
								'src="http://www.gravatar.com/avatar/'+ v.avatar +'?s=20&d=mm"'+
								'width="20" height="20"'+
								'alt="Avatar" />'+
						'</a>'+
					'</div>');
			}

			// update avatars globally
			_update(v.id, v.status);

			// update location on profile
			$("#profile #location[data-id='" + v.id + "']").html('<a data-to="ajax" href="' + v.location.actual + '">' + v.location.title + '</a>');
		});

	// set user as offline
	}, _offline = function(id) {
		// remove from top
		$("div.avatar[data-id='" + id + "']", 'div.inside > div.top').remove();
		_update(id, 'offline');

	// update avatars globally
	}, _update = function(id, status) {
		// go through each avatar
		var update = false;
		$("div.avatar[data-id='" + id + "']").each(function() {
			var self = $(this),
				cont = self.find("div.status"),
				title = self.find("a");

			// needs update?
			if(!cont.hasClass(status)) {
				update = true;

				// update status
				cont.removeClass('offline online away busy').addClass(status);

				// update title
				if(!title.length > 0)
					return false;

				// update title
				var arr = title.attr('title').split(' - ');
				title.attr('title', arr[0] + ' - ' + status.charAt(0).toUpperCase() + status.slice(1));
			}
		});

		// trigger online event (which we listen for in Frontend._bind)
		if(update)
			$('div.page', 'div.site').trigger('online');

	// on rank change
	}, _rank = function(tokens) {
		// guest? don't even bother
		var token = _token();
		if(!token)
			return;

		$.each(tokens, function(k, v){
			// if our token doesnt match what we have 'continue'
			if(token != v)
				return true;

			// send message & refresh metro
			Frontend.noty({type: 'information', text: 'Your rank was changed!'});
			Frontend.metro();
		});

	// bind events
	}, _bind = function() {
		socket.on('announce', function(result) {
			// load module announce
			if(Frontend.ctrl[Frontend.location.ctrl]['announce'])
				Frontend.ctrl[Frontend.location.ctrl]['announce'](result);

			// load default announce
			else {
				// if we are on the same page, refresh it
				if(Frontend.announce(result))
					Frontend.go(window.location.href);
			}
		});

		// on metro refresh metro :)
		socket.on('metro', function(data) {
			Frontend.metro();
		});

		// on noty show it and play sound if not busy
		socket.on('noty', function(data) {
			Frontend.noty(data, store.away);
			if(!store.busy)
				store.sound.noty.play();
		});

		// on rank change
		socket.on('rank', _rank);

		// on user status change
		socket.on('away', _away);
		socket.on('busy', _busy);
		socket.on('online', _online);
		socket.on('offline', _offline);

	// simple emit secured with token
	}, _emit = function(what, data) {
		// no token? no business
		var token = _token();
		if(!token)
			return;

		// emit it
		socket.emit(what, {
			token: token,
			data: data
		});

	// announce
	}, announce = function(data) {
		_emit('announce', data);

	// metro
	}, metro = function(data) {
		_emit('metro', data);

	// noty
	}, noty = function(data) {
		_emit('noty', data);

	// rank
	}, rank = function(data) {
		_emit('rank', data);

	// set away
	}, away = function(prevent) {
		// no token? no business
		var token = _token();
		if(!token)
			return;

		// set as away if not busy
		if(!store.busy)
			store.away = prevent ? true : !store.away;

		// broadcast away or online?
		if(store.away) socket.emit('away', token);
		else Frontend.login();

	// set busy
	}, busy = function() {
		// no token? no business
		var token = _token();
		if(!token)
			return;

		// busy toggle and take away away :P
		store.busy = !store.busy;
		store.away = false;

		// broadcast busy or online?
		if(store.busy) socket.emit('busy', token);
		else Frontend.login();

	// login
	}, login = function() {
		// no token? no business
		var token = _token();
		if(!token)
			token = 'guest';

		// clear away state
		store.away = false;

		// broadcast join
		socket.emit('join', {
			token: token,
			status: store.busy ? 'busy' : 'online',
			active: Frontend.active,
			location: Frontend.location
		});

	// logout
	}, logout = function() {
		// clear away state and busy
		store.away = false;
		store.busy = false;

		// broadcast leave
		socket.emit('leave');

	// constructor
	}, __construct = function() {
		_sounds();

		// start socket io
		socket = io.connect(':8080');
		socket.on('connect', _bind);
	};

	// public, yay
	return {
		connect: __construct,
		announce: announce,
		metro: metro,
		noty: noty,
		rank: rank,
		away: away,
		busy: busy,
		login: login,
		logout: logout
	};
}();

// defaults
Frontend.ctrl = {};
Frontend.focus = true;
Frontend.away = 60000; // 1 minute
Frontend.active = new Date().getTime();
Frontend.location = {ctrl: null, path: []};

// init frontend on ready
Frontend.socket.connect();
$(document).ready(Frontend.init);